﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using UBCon.Ribbon.Interfaces;

namespace UBCon.Ribbon
{
    internal class RibbonWrapPanel : Panel, IRibbonControl
    {
        #region Constructors

        public RibbonWrapPanel()
        {

        }
        #endregion

        #region Properties and Depenency Properties

        public System.Windows.Controls.Orientation Orientation
        {
            get { return (System.Windows.Controls.Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Orientation.  This enables animation, styling, binding, etc...
        public static readonly System.Windows.DependencyProperty OrientationProperty =
            System.Windows.DependencyProperty.Register("Orientation", typeof(System.Windows.Controls.Orientation), typeof(RibbonWrapPanel), new System.Windows.FrameworkPropertyMetadata(System.Windows.Controls.Orientation.Vertical));

        public int WrapBy
        {
            get { return (int)GetValue(WrapByProperty); }
            set { SetValue(WrapByProperty, value); }
        }

        // Using a DependencyProperty as the backing store for WrapBy.  This enables animation, styling, binding, etc...
        public static readonly System.Windows.DependencyProperty WrapByProperty =
            System.Windows.DependencyProperty.Register("WrapBy", typeof(int), typeof(RibbonWrapPanel), new System.Windows.FrameworkPropertyMetadata(3, System.Windows.FrameworkPropertyMetadataOptions.AffectsMeasure, new System.Windows.PropertyChangedCallback(OnWrapByPropertyChanged)));

        internal int Interval
        {
            get { return (int)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Interval.  This enables animation, styling, binding, etc...
        internal static readonly System.Windows.DependencyProperty IntervalProperty =
            System.Windows.DependencyProperty.Register("Interval", typeof(int), typeof(RibbonWrapPanel), new System.Windows.PropertyMetadata(1));


        #endregion

        #region Depenency Property Changed Callbacks

        private static void OnWrapByPropertyChanged(System.Windows.DependencyObject o, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            int newValue = (int)e.NewValue;
            if (newValue == 1)
            {
                o.SetValue(RibbonWrapPanel.OrientationProperty, System.Windows.Controls.Orientation.Vertical);
            }
        }
        #endregion

        #region Overrieds
        protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
        {

            double max_y = 0, max_x = 0, y = 0, x = 0;
            System.Windows.Size size = new System.Windows.Size();
            System.Windows.UIElement e = null;

            if (this.WrapBy == 1)
            {
                size = this.ArrangeAsStackPanel(finalSize);
                return size;
            }
            if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                for (int i = 0; i < this.InternalChildren.Count; i++)
                {
                    if (i != 0 && i % this.WrapBy == 0)
                    {
                        size.Width += x;
                        x = size.Width;
                        y += max_y;
                        x = 0;
                        max_y = 0;
                    }
                    e = this.InternalChildren[i];
                    e.Arrange(new System.Windows.Rect(x, y, e.DesiredSize.Width, e.DesiredSize.Height));
                    x += e.DesiredSize.Width + this.Interval;
                    max_y = System.Math.Max(max_y, e.DesiredSize.Height);
                }
                y += max_y;
                size.Width += x;
                size.Height = System.Math.Max(size.Height, y);
            }
            else
            {
                for (int i = 0; i < this.InternalChildren.Count; i++)
                {
                    if (i != 0 && i % this.WrapBy == 0)
                    {
                        size.Width += max_x;
                        x = size.Width;
                        max_y = System.Math.Max(max_y, y);
                        max_x = 0;
                        y = 0;
                    }
                    e = this.InternalChildren[i];
                    e.Arrange(new System.Windows.Rect(x, y, e.DesiredSize.Width, e.DesiredSize.Height));
                    y += e.DesiredSize.Height + this.Interval;
                    max_x = System.Math.Max(max_x, e.DesiredSize.Width);
                }
                size.Width += max_x;
                max_y = System.Math.Max(max_y, y);
                size.Height = System.Math.Max(size.Height, max_y);
            }
            return size;

        }
        protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
        {
            System.Windows.Size infiniteSize = new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity);
            foreach (System.Windows.UIElement elt in this.InternalChildren)
            {
                elt.Measure(infiniteSize);
            }
            System.Windows.Size size = System.Windows.Size.Empty;
            if (this.WrapBy == 1)
            {
                size = this.MeasureAsStackPanel(availableSize);
                return size;
            }
            size = this.CalculateExtent();
            if (!double.IsInfinity(availableSize.Height))
            {
                size.Height = System.Math.Max(size.Height, availableSize.Height);
            }

            return size;
        }

        internal System.Windows.Size MeasureAsStackPanel(System.Windows.Size availableSize)
        {
            double w = 0;
            System.Windows.Size size = new System.Windows.Size();
            if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                foreach (System.Windows.UIElement item in this.InternalChildren)
                {
                    w += item.DesiredSize.Width + this.Interval;
                }
                if (availableSize.Width == double.PositiveInfinity)
                {
                    size.Width = w;
                }
                else
                {
                    size.Width = availableSize.Width;
                }

                size.Height = this.GetMaxHeight();


            }
            else
            {
                foreach (System.Windows.UIElement item in this.InternalChildren)
                {
                    w += item.DesiredSize.Height + this.Interval;
                }
                if (availableSize.Height == double.PositiveInfinity)
                {
                    size.Height = w;
                }
                else
                {
                    size.Height = System.Math.Min(availableSize.Height, w);
                }
                size.Width = this.GetMaxWidth();
            }
            return size;
        }

        internal System.Windows.Size ArrangeAsStackPanel(System.Windows.Size finalSize)
        {
            double x = 0;

            if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                foreach (System.Windows.UIElement item in this.InternalChildren)
                {
                    item.Arrange(new System.Windows.Rect(x, 0, item.DesiredSize.Width, finalSize.Height));
                    x += item.DesiredSize.Width + this.Interval;
                }
            }
            else
            {
                foreach (System.Windows.UIElement item in this.InternalChildren)
                {
                    item.Arrange(new System.Windows.Rect(0, x, finalSize.Width, item.DesiredSize.Height));
                    x += item.DesiredSize.Height + this.Interval;
                }
            }

            return finalSize;
        }

        private double GetMaxHeight()
        {
            double max = 0;
            foreach (System.Windows.UIElement item in this.InternalChildren)
            {
                max = System.Math.Max(item.DesiredSize.Height, max);

            }
            return max;
        }

        private double GetMaxWidth()
        {
            double max = 0;
            foreach (System.Windows.UIElement item in this.InternalChildren)
            {
                max = System.Math.Max(item.DesiredSize.Width, max);

            }
            return max;
        }
        #endregion

        #region Private Utilities

        private System.Windows.Size CalculateExtent()
        {
            double max_y = 0, max_x = 0, y = 0, x = 0;
            System.Windows.UIElement e = null;
            System.Windows.Size size = new System.Windows.Size();
            if (this.Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                for (int i = 0; i < this.InternalChildren.Count; i++)
                {
                    e = this.InternalChildren[i];

                    if (i != 0 && i % this.WrapBy == 0)
                    {
                        size.Width = System.Math.Max(size.Width, x);
                        size.Height += max_y;
                        max_y = 0;
                        x = 0;
                    }
                    x += e.DesiredSize.Width + this.Interval;
                    max_y = System.Math.Max(max_y, e.DesiredSize.Height);
                }
                size.Width = System.Math.Max(size.Width, x);
                size.Height += max_y;
            }
            else
            {
                for (int i = 0; i < this.InternalChildren.Count; i++)
                {
                    e = this.InternalChildren[i];

                    if (i != 0 && i % this.WrapBy == 0)
                    {
                        size.Width += max_x;
                        size.Height = System.Math.Max(y, size.Height);
                        max_x = 0;
                        y = 0;
                    }
                    y += e.DesiredSize.Height + this.Interval;
                    max_x = System.Math.Max(max_x, e.DesiredSize.Width);
                }
                size.Width += max_x;
                size.Height = System.Math.Max(y, size.Height);
            }
            return size;
        }

        #endregion

        #region Event Handlers
        #endregion

        #region Fields
        #endregion

        #region IRibbonControl Implementation

        /// <summary>
        /// Gets a boolean value indicating whether the contol's size is large
        /// </summary>
        public bool IsLarge
        {
            get { return false; }
        }

        #endregion
    }
}
